package com.starsoft.trade.service;

import static com.starsoft.frame.util.NumberUtil.addNullable;
import static com.starsoft.frame.util.NumberUtil.null2Zero;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.starsoft.frame.core.orm.PageObject;
import com.starsoft.frame.util.DateUtil;
import com.starsoft.frame.util.MapUtil;
import com.starsoft.frame.util.StringUtil;
import com.starsoft.smdc.bean.SmdcMarketDaily;
import com.starsoft.smdc.bean.SmdcSecurity;
import com.starsoft.smdc.service.HolidayService;
import com.starsoft.smdc.service.MarketDailyService;
import com.starsoft.smdc.service.SecurityService;
import com.starsoft.smdc.util.SecurityType;
import com.starsoft.trade.bean.TradeBill;
import com.starsoft.trade.dao.TradeBillDao;
import com.starsoft.trade.util.TradeType;

@Service
public class TradeBillService {
	
	@Autowired
	TradeBillDao billDao;
	@Autowired
	TradeFeeService feeService;
	@Autowired
	SecurityService securityService;
	@Autowired
	HolidayService holidayService;
	@Autowired
	MarketDailyService marketService;
	
	public void saveOrUpdate(TradeBill bill) {
		this.checkField(bill);
		this.dealMoney(bill);
		this.dealTime(bill);
		billDao.saveOrUpdate(bill);
	}
	
	public void saveOrUpdate(TradeBill bill, boolean dealMoney) {
		this.checkField(bill);
		if (dealMoney || bill.getBillMoney() == null) {
			this.dealMoney(bill);
		}
		this.dealTime(bill);
		billDao.saveOrUpdate(bill);
	}
	
	public void saveOrUpdateFeeMannul(TradeBill bill) {
		this.checkField(bill);

		TradeType tradeType = TradeType.get(bill);
		if (tradeType.isSellBuy()) {
			if (tradeType == TradeType.bondbuy || tradeType == TradeType.bondsell) {
				bill.setBondInterest(securityService.getBondInterset(bill.getSecId(), bill.getBillTime()));
			}
			double tradeMoney = -bill.getSecNum() * (bill.getSecPrice() + null2Zero(bill.getBondInterest()));
			bill.setBillTradeMoney(tradeMoney);
		}
		bill.setFeeAll(addNullable(bill.getFeeCommision(), bill.getFeeTax(), bill.getFeeOther()));
		bill.setBillMoney(bill.getBillTradeMoney() - null2Zero(bill.getFeeAll()));

		this.dealTime(bill);
		billDao.saveOrUpdate(bill);
	}
	
	public void delete(TradeBill bill){
		if(bill.getBillId()!=null){
			billDao.delete(TradeBill.class, bill.getBillId());
		}
	}

	public void delete(Integer... billIds){
		billDao.delete(TradeBill.class , (Serializable[]) billIds);
	}
	
	public TradeBill get(Integer billId){
		return billDao.get(billId);
	}
	
	public List<TradeBill> getBills(Integer acountId, Date billDate){
		return billDao.getBills(acountId, billDate, billDate);
	}
	
	public List<TradeBill> getBills(Integer acountId, String secId, Date billDate){
		return billDao.getBills(acountId, billDate, billDate);
	}

	public List<TradeBill> getBills(Integer acountId, Date beginDate, Date endDate){
		return billDao.getBills(acountId, beginDate, endDate);
	}
	
	public void checkField(TradeBill bill){
		if(bill.getBillType()==null){
			throw new RuntimeException("交易类型为空");
		}
		if(bill.getAcountId()==null){
			throw new RuntimeException("账户为空");
		}
		Timestamp billTime = bill.getBillTime();
		if(billTime==null || !holidayService.isBusinessDay(billTime)){
			throw new RuntimeException("交易时间不正确");
		}
		TradeType tradeType = TradeType.get(bill);
		if(tradeType==TradeType.transfer || tradeType==TradeType.oci){
			return;
		}
		if(bill.getSecId()==null){
			throw new RuntimeException("证券ID为空");
		}
		SmdcSecurity security = securityService.get(bill.getSecId());
		SecurityType secType = SecurityType.get(security);
		SecurityType secTypeT = tradeType.getSecType();
		if(!secType.equals(secTypeT)){
			throw new RuntimeException("交易类型与证券类型不符:"+tradeType.name()+" "+security.getSecName());
		}
		if(tradeType.isSellBuy()){
			if(bill.getSecNum()==null){
				throw new RuntimeException("交易股数不正确");
			}
			if(bill.getSecPrice()==null){
				throw new RuntimeException("交易价格不正确");
			}
		}
		if (tradeType.isSell() && bill.getSecNum() > 0) {
			bill.setSecNum(-bill.getSecNum());
		}
		if (bill.getSecNum() == null) {
			bill.setSecNum(0d);
		}
	}
	
	public void dealBillType(TradeBill bill) {
		if (bill.getBillType() == null && bill.getSecId() != null) {
			SmdcSecurity security = securityService.get(bill.getSecId());
			SecurityType secType = SecurityType.get(security);
			TradeType tradeType = null;
			if(secType == SecurityType.stock){
				if(bill.getSecNum()>0) tradeType = TradeType.stockbuy;
				else if(bill.getSecNum()<0) tradeType = TradeType.stocksell;
				else tradeType = TradeType.stockdiv;
			} else if(secType == SecurityType.fund){
				if(bill.getSecNum()>0) tradeType = TradeType.fundbuy;
				else if(bill.getSecNum()<0) tradeType = TradeType.fundsell;
				else tradeType = TradeType.funddiv;
			} else if(secType == SecurityType.index){
				if(bill.getSecNum()>0) tradeType = TradeType.indexbuy;
				else tradeType = TradeType.indexsell;
			} else if(secType == SecurityType.bond){
				if(bill.getSecNum()>0) tradeType = TradeType.bondbuy;
				else tradeType = TradeType.bondsell;
			} else {
				throw new RuntimeException("not support security type:" + secType);
			}
			bill.setBillType(tradeType.name());
		}
	}
	
	public void dealMoney(TradeBill bill) {
		TradeType tradeType = TradeType.get(bill);
		if (tradeType.isSellBuy()) {
			if (tradeType == TradeType.bondbuy || tradeType == TradeType.bondsell) {
				bill.setBondInterest(securityService.getBondInterset(bill.getSecId(), bill.getBillTime()));
			}
			double tradeMoney = -bill.getSecNum() * (bill.getSecPrice() + null2Zero(bill.getBondInterest()));
			bill.setBillTradeMoney(tradeMoney);
		}
		feeService.setFee(bill);
		bill.setBillMoney(bill.getBillTradeMoney() - null2Zero(bill.getFeeAll()));
	}
	
	public void dealTime(TradeBill bill){
		Timestamp ts= DateUtil.getTimestamp();
		bill.setUpdateTime(ts);
		if(bill.getBillId()==null){
			bill.setCreateTime(ts);
		}
	}
	
	public Map<String, Object> pageQueryBills(PageObject po, Integer acountId, String secId, String billType, Date beginDate, Date endDate){
		return billDao.pageQuery(po, acountId, secId, billType, beginDate, endDate);
	}
	
	public void deleteBillsAfter(Integer acountId, Date startDate){
		String hql = "delete TradeBill where acountId = ?0 and billTime >= ?1";
		billDao.update(hql, acountId, startDate);
	}
	
	public void deleteBillsBefore(Integer acountId, Date endDate){
		String hql = "delete TradeBill where acountId = ?0 and billTime <= ?1";
		billDao.update(hql, acountId, endDate);
	}

	public Date getSmallestBillTime(Integer acountId, Integer[] billIds) {
		String hql = "select min(billTime) from TradeBill where  acountId = :acountId and billId in (:billIds)";
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("acountId", acountId);
		params.put("billIds", billIds);
		Object result = billDao.getValueByMap(hql, params);
		return (Date) result;
	}
	
	public Date getLastBillDate(Integer acountId){
		String hql = "select max(billTime) from TradeBill where  acountId = ?0";
		Object result = billDao.getValue(hql, acountId);
		return (Date) result;
	}
	
	public List<Map<String, Object>> convertToMap(List<TradeBill> bills) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		for (TradeBill bill : bills) {
			Map<String, Object> billMap = MapUtil.toMap(bill);
			String secId = bill.getSecId();
			if (!StringUtil.isEmpty(secId)) {
				String secName = securityService.get(bill.getSecId()).getSecName();
				billMap.put("secName", secName);
			}
			if(TradeType.get(bill).isSellBuy()) {
				Date lastDate = holidayService.getNextBusinessDay(bill.getBillTime(), -1);
				SmdcMarketDaily market = marketService.getMarketDaily(secId, lastDate);
				if(!StringUtil.isEmpty(market.getSecNav())) {
					Double tradeDiscount = (1 - bill.getSecPrice() / market.getSecNav()) * 100;
					billMap.put("tradeDiscount", tradeDiscount);
				}
			}
			result.add(billMap);
		}
		return result;
	}
	
	public TradeBill getBillBySerialnumber(Integer acountId, Integer serialnumber){
		String hql = "from TradeBill where acountId=?0 and billSerialnumber=?1";
		return billDao.get(hql, acountId, serialnumber);
	}
}
